//-----------------------------------------------------------------------
// <copyright file="MLUtils.cs" company="MAPILab Ltd">
//     Copyright (c) MAPILab Ltd.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace MAPILab.SharePoint.Explorer.Utilities
{
    using System;
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.IO;
    using System.Text.RegularExpressions;
    using System.Windows.Forms;
    using MAPILab.SharePoint.Explorer.ObjectBrowser.TreeExpand;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;
    using Microsoft.SharePoint.Upgrade;

    #region Enums

    /// <summary>
    /// Code language
    /// </summary>
    internal enum CodeLanguage
    {
        /// <summary>
        /// C# code language
        /// </summary>
        CSharp,
        /// <summary>
        /// VB.Net code language
        /// </summary>
        VBNet
    }

    #endregion

    /// <summary>
    /// Different utilites
    /// </summary>
    static class MLUtils
    {
        #region Method

        /// <summary>
        /// Get first public type from base types
        /// </summary>
        /// <param name="paramType"></param>
        /// <returns></returns>
        public static Type GetType(Type paramType)
        {
            Type type = paramType;
            if (type.IsNotPublic)
            {
                type = GetType(type.BaseType);
            }

            return type;
        }

        /// <summary>
        /// Gets the full name of the type to use in code template
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetTypeOutput(Type type)
        {
            CodeDomProvider codeProvider = GetCodeProvider();

            CodeTypeReference ctr = new CodeTypeReference(type);

            return codeProvider.GetTypeOutput(ctr);
        }

        /// <summary>
        /// Gets code provider, that depends on selected as default code language
        /// </summary>
        /// <returns></returns>
        public static CodeDomProvider GetCodeProvider()
        {
            CodeDomProvider codeProvider;
            switch (Properties.Settings.Default.CodeLanguage)
            {
                case "VBNet": codeProvider = new Microsoft.VisualBasic.VBCodeProvider();
                    break;

                case "CShrap":
                default: codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
                    break;
            }
            return codeProvider;
        }

        /// <summary>
        /// Gets code provider, that depends on set language
        /// </summary>
        /// <returns></returns>
        public static CodeDomProvider GetCodeProvider(CodeLanguage codeLanguage)
        {
            CodeDomProvider codeProvider;
            switch (codeLanguage)
            {
                case CodeLanguage.VBNet: codeProvider = new Microsoft.VisualBasic.VBCodeProvider();
                    break;

                case CodeLanguage.CSharp:
                default: codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
                    break;
            }
            return codeProvider;
        }

        /// <summary>
        /// Gets the code language from extention
        /// </summary>
        /// <param name="scriptFile"></param>
        /// <returns></returns>
        public static CodeLanguage GetCodeLanguage(string scriptFile)
        {
            CodeLanguage codeLanguage;
            if (scriptFile.EndsWith(Properties.Resources.VBNetExtention))
            {
                codeLanguage = CodeLanguage.VBNet;
            }
            else
            {
                codeLanguage = CodeLanguage.CSharp;
            }
            return codeLanguage;
        }

        /// <summary>
        /// Loading code from file
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static string LoadCode(string fileName)
        {
            string text = null;

            try
            {
                using (StreamReader reader = new StreamReader(fileName))
                {
                    text = reader.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return text;
        }

        /// <summary>
        /// Create script and store it in script manager
        /// </summary>
        /// <param name="o">object that should be returnen in script</param>
        public static void CreateScript(object o, CodeLanguage codeLanguage)
        {
            string fileName = String.Empty;
            string text = String.Empty;

            StreamWriter writer = null;
            string root = GetRoot();
            DirectoryInfo directory = new DirectoryInfo(root);
            if (!directory.Exists)
            {
                directory.Create();
            }

            if (codeLanguage == CodeLanguage.VBNet)
            {
                AddVBNetScript(o, ref fileName, ref writer, root);
            }
            else
            {
                AddCSharpScript(o, ref fileName, ref writer, root);
            }

            if (writer != null)
                writer.Close();

            if (Properties.Settings.Default.storedScripts == null)
                Properties.Settings.Default.storedScripts = new StringCollection();

            if (Properties.Settings.Default.startupScripts == null)
                Properties.Settings.Default.startupScripts = new StringCollection();

            if (!Properties.Settings.Default.storedScripts.Contains(fileName) && 
                !Properties.Settings.Default.startupScripts.Contains(fileName))
            {
                Properties.Settings.Default.storedScripts.Add(fileName);
                Properties.Settings.Default.Save();

                text = "Script has been successfully added as stored";
            }
            else
            {
                text = "Old script has been replaced!";
            }

            MessageBox.Show(text, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);

        }

        /// <summary>
        /// Add CSharp script to the file and save it
        /// </summary>
        /// <param name="o">Object that should be returned from script</param>
        /// <param name="fileName">file name of script</param>
        /// <param name="writer">Stream to write in</param>
        /// <param name="root">Root where script should be saved</param>
        private static void AddCSharpScript(object o, ref string fileName, ref StreamWriter writer, string root)
        {
            string extention = Properties.Resources.CSharpExtention;

            string assemblies = @"///addref<" + Application.ExecutablePath + ">\n" +
                @"///addref<" + GetSharePointDLLPath() + ">";
            try
            {
                if (o is SPFarm)
                {
                    fileName = "SPFarm" + extention;
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.CSharpHead + Properties.Resources.CSharpSPFarm.Replace(@"/*Text*/", fileName));
                }
                else if (o is SPManager)
                {
                    fileName = "SPManager" + extention;
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.CSharpHead + Properties.Resources.CSharpSPManager.Replace(@"/*Text*/", fileName));
                }
                else if (o is SPTimerService)
                {
                    fileName = "SPTimerService" + extention;
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.CSharpHead + Properties.Resources.CSharpSPTimerService.Replace(@"/*Text*/", fileName));
                }
                else if (o is SPWebApplication)
                {
                    fileName = "SPWebApplication_" + (o as SPWebApplication).Name.Replace(' ', '_').Replace(':', '_').Replace('/', '_') + extention;
                    string id = (o as SPWebApplication).Id.ToString();
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.CSharpHead + Properties.Resources.CSharpWebApplication.Replace(@"/*Text*/", fileName).Replace(@"/*guid*/", id));
                }
                else if (o is SPSite)
                {
                    fileName = "SPSite_" + (o as SPSite).Url.Remove(0, 7).Replace(':', '_').Replace('/', '_') + extention;
                    string id = (o as SPSite).ID.ToString();
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.CSharpHead + Properties.Resources.CSharpSite.Replace(@"/*Text*/", fileName).Replace(@"/*SiteID*/", id));
                }
                else if (o is SPWeb)
                {
                    fileName = "SPWeb_" + (o as SPWeb).Url.Remove(0, 7).Replace(':', '_').Replace('/', '_') + extention;
                    string WebId = (o as SPWeb).ID.ToString();
                    string SiteId = (o as SPWeb).Site.ID.ToString();
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.CSharpHead + Properties.Resources.CSharpWeb.Replace(@"/*Text*/", fileName).Replace(@"/*SiteID*/", SiteId).Replace(@"/*WebID*/", WebId));
                }
                else if (o is SPPersistedObject)
                {
                    fileName = "SPPersistedObject_" + (o as SPPersistedObject).DisplayName + "_" + (o as SPPersistedObject).Id.ToString() + extention;
                    string id = (o as SPPersistedObject).Id.ToString();
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.CSharpHead + Properties.Resources.CSharpSPPersistedObject.Replace(@"/*Text*/", fileName).Replace(@"/*SPPersistedObjectID*/", id));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Add VBNet script to the file and save it
        /// </summary>
        /// <param name="o">Object that should be returned from script</param>
        /// <param name="fileName">file name of script</param>
        /// <param name="writer">Stream to write in</param>
        /// <param name="root">Root where script should be saved</param>
        private static void AddVBNetScript(object o, ref string fileName, ref StreamWriter writer, string root)
        {
            string extention = Properties.Resources.VBNetExtention;

            string assemblies = @"'addref<" + Application.ExecutablePath + ">\n" +
                @"'addref<" + GetSharePointDLLPath() + ">";
            try
            {
                if (o is SPFarm)
                {
                    fileName = "SPFarm" + extention;
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.VBNetHead + Properties.Resources.VBNetSPFarm.Replace(@"/*Text*/", fileName));
                }
                else if (o is SPManager)
                {
                    fileName = "SPManager" + extention;
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.VBNetHead + Properties.Resources.VBNetSPManager.Replace(@"/*Text*/", fileName));
                }
                else if (o is SPTimerService)
                {
                    fileName = "SPTimerService" + extention;
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.VBNetHead + Properties.Resources.VBNetSPTimerService.Replace(@"/*Text*/", fileName));
                }
                else if (o is SPWebApplication)
                {
                    fileName = "SPWebApplication_" + (o as SPWebApplication).Name.Replace(' ', '_').Replace(':', '_').Replace('/', '_') + extention;
                    string id = (o as SPWebApplication).Id.ToString();
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.VBNetHead + Properties.Resources.VBNetSPWebApplication.Replace(@"/*Text*/", fileName).Replace(@"/*guid*/", id));
                }
                else if (o is SPSite)
                {
                    fileName = "SPSite_" + (o as SPSite).Url.Remove(0, 7).Replace(':', '_').Replace('/', '_') + extention;
                    string id = (o as SPSite).ID.ToString();
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.VBNetHead + Properties.Resources.VBNetSPSite.Replace(@"/*Text*/", fileName).Replace(@"/*SiteID*/", id));
                }
                else if (o is SPWeb)
                {
                    fileName = "SPWeb_" + (o as SPWeb).Url.Remove(0, 7).Replace(':', '_').Replace('/', '_') + extention;
                    string WebId = (o as SPWeb).ID.ToString();
                    string SiteId = (o as SPWeb).Site.ID.ToString();
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.VBNetHead + Properties.Resources.VBNetSPWeb.Replace(@"/*Text*/", fileName).Replace(@"/*SiteID*/", SiteId).Replace(@"/*WebID*/", WebId));
                }
                else if (o is SPPersistedObject)
                {
                    fileName = "SPPersistedObject_" + (o as SPPersistedObject).DisplayName + "_" + (o as SPPersistedObject).Id.ToString() + extention;
                    string id = (o as SPPersistedObject).Id.ToString();
                    writer = new StreamWriter(root + fileName);
                    writer.Write(assemblies + Properties.Resources.VBNetHead + Properties.Resources.VBNetSPPersistedObject.Replace(@"/*Text*/", fileName).Replace(@"/*SPPersistedObjectID*/", id));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Returns root of stored scripts
        /// </summary>
        /// <returns>root of scripts</returns>
        public static string GetRoot()
        {
            return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\MAPILab_Ltd\SPExplorer\QuickLunch\";
        }

        /// <summary>
        /// Add rules to display items of enumerable objects
        /// </summary>
        public static void AddRules()
        {
            List<MLDisplayRule> rules = new List<MLDisplayRule>(Properties.Settings.Default.rulesToDisplay.Count);

            foreach (string rule in Properties.Settings.Default.rulesToDisplay)
            {
                Regex mask = new Regex(@"^ *(?<type>[a-z\.]+) *= *(?<name>[a-z\.]+) *(\: *(?<properties>[a-z_\;\%\=\(\) ]+))? *$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

                Match m = mask.Match(rule);
                if (m.Success)
                {
                    string type = m.Groups["type"].Value;
                    string name = m.Groups["name"].Value;
                    List<MLRuleProperty> properties;

                    Regex maskParams = new Regex(@"(?<title>[a-z\.]+) *= *\%(?<name>[a-z_]+(\(\))?)\% *;", RegexOptions.IgnoreCase);
                    MatchCollection mc = maskParams.Matches(m.Groups["properties"].Value);
                    properties = new List<MLRuleProperty>(mc.Count);

                    if (mc.Count > 0)
                        foreach (Match property in mc)
                        {
                            string propertyName = property.Groups["name"].Value;
                            if (propertyName.EndsWith("()"))
                            {
                                properties.Add(new MLRuleProperty(propertyName.Substring(0, propertyName.Length - 2), property.Groups["title"].Value, MLRuleProperty.MLType.Method, false));
                            }
                            else
                            {
                                properties.Add(new MLRuleProperty(propertyName, property.Groups["title"].Value, MLRuleProperty.MLType.Property, false));
                            }
                        }

                    rules.Add(new MLDisplayRule(type, name, properties));
                }
            }

            MLObjectTreeExpand.Rules = rules;
        }

        /// <summary>
        /// Returns path to Microsoft.SharePoint.dll stored at the assambly folder
        /// </summary>
        /// <returns>Path to Microsoft.SharePoint.dll</returns>
        public static string GetSharePointDLLPath()
        {
            int spVersion = SPFarm.Local.BuildVersion.Major;
            string SharePoint_dllPath = String.Empty;
            if (spVersion < 15)
            {
                SharePoint_dllPath = Environment.SystemDirectory.Replace("system32", "") +
                @"assembly\GAC_MSIL\Microsoft.SharePoint\" +
                spVersion +
                @".0.0.0__71e9bce111e9429c\Microsoft.SharePoint.dll";
            }
            else
            {
                SharePoint_dllPath = Environment.SystemDirectory.Replace("system32", "") +
                @"Microsoft.Net\assembly\GAC_MSIL\Microsoft.SharePoint\V4.0_" +
                spVersion +
                @".0.0.0__71e9bce111e9429c\Microsoft.SharePoint.dll";
            }
            return SharePoint_dllPath;
        }

        #endregion
    }
}
